
<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport"
		content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
	<title>virtualScroll</title>
	<link rel="icon" type="image/x-icon" href="../images/favicon.ico">
	<style>
		* {
			margin: 0;
			padding: 0;
		}

		.virtual-scroll-viewport {
			position: relative;
			width: 240px;
			height: 300px;
			margin: 150px auto 0;
			overflow: auto;
			will-change: scroll-position;
			border: 1px solid #aaaaaa;
		}

		/* 撑开高度 */
		.virtual-scroll-spacer {
			position: absolute;
			top: 0;
			left: 0;
			right: 0;
		}

		.virtual-scroll-list {
			position: absolute;
			top: 0;
			left: 0;
			right: 0;
		}

		.virtual-scroll-item {
			height: 50px;
			padding-left: 15px;
			line-height: 50px;
		}

		.virtual-scroll-item:nth-child(2n) {
			background: #f6f8fa;
		}
	</style>
</head>

<body>
	<div class="virtual-scroll-viewport">
		<div class="virtual-scroll-spacer"></div>
		<div class="virtual-scroll-list"></div>
	</div>

	<script>
		// 获取dom
		const virtualViewportDom = document.querySelector('.virtual-scroll-viewport');
		const virtualSpacerDom = document.querySelector('.virtual-scroll-spacer');
		const virtualListDom = document.querySelector('.virtual-scroll-list');

		const VIEWPORT_HEIGHT = 300; // 视口高度
		const ITEM_HEIGHT = 50; // 列表项高度
		const COUNT = 10; // 渲染数量
		const TOTAL_COUNT = 100; // 总条数
		const TOTAL_HEIGHT = ITEM_HEIGHT * TOTAL_COUNT; // 总高度
		let scrollTop = 0, translateY = 0;
		virtualSpacerDom.style.height = TOTAL_HEIGHT + 'px';
		// 监听scroll事件
		virtualViewportDom.addEventListener('scroll', function (e) {
            
			scrollTop = this.scrollTop;
            console.clear()
            console.log(scrollTop,'scrollTop');
			const start = Math.max(Math.floor(scrollTop / ITEM_HEIGHT) - 2, 0);
            console.log(start,'start');
			const end = Math.min(start + COUNT, TOTAL_COUNT);
            console.log(end,'end');
			const y = start * ITEM_HEIGHT;
            console.log(y,'y');
			if (scrollTop === 0 || scrollTop === TOTAL_HEIGHT - VIEWPORT_HEIGHT || Math.abs(translateY - y) >= 100) {
				render(start, end);
                console.log(y,'adadasdas');
				translateY = y;
				virtualListDom.style.transform = 'translate3d(0px, ' + translateY + 'px, 0px)';
			}
		});
		// 初次渲染
		render(0, COUNT);

		function render(start, end) {
			let html = '';
			// 移除多余的dom
            // 第一步这里不执行 先跳过
			let list = document.querySelectorAll('.virtual-scroll-item');
			for (const item of list) {
				const index = Number(item.dataset.index);
				if (index < start || index >= end) {
					item.remove();
				}
			}

			list = document.querySelectorAll('.virtual-scroll-item');

			if (list.length > 0) {
				const lastStart = Number(list[0].dataset.index);
				const lastEnd = Number(list[list.length - 1].dataset.index);
				if (end - 1 - lastEnd > 0) { // 下滑
					for (let i = lastEnd + 1; i < end; i++) {
						html += '<div class="virtual-scroll-item" data-index="' + i + '">item' + i + '</div>';
					}
					virtualListDom.insertAdjacentHTML('beforeend', html);
				} else if (start - lastStart < 0) { // 上滑
					for (let i = start; i < lastStart; i++) {
						html += '<div class="virtual-scroll-item" data-index="' + i + '">item' + i + '</div>';
					}
					virtualListDom.insertAdjacentHTML('afterbegin', html);
				}
			} else {
				// 快速滑动或拖动滚动条或初次渲染
				for (let i = start; i < end; i++) {
					html += '<div class="virtual-scroll-item" data-index="' + i + '">item' + i + '</div>';
				}
				virtualListDom.innerHTML = html;
			}
            console.log(virtualListDom);
		}
	</script>
</body>

</html>